Scraping a Wikipedia Page
First, open this Wikipedia page in Chrome and scroll down till you come to this table:

This is the data we want to extract. Each row corresponds to a date, each column corresponds to one of the US states and each cell has the number of new COVID-19 cases reported on that date in that state. We want to scrape this table from the page and make it into a dataframe.
To start, let’s load a few familiar packages and assign the URL of the webpage to an R variable.
# load the usual packages
library(dplyr)
library(tidyr)
library(ggplot2)
# the URL of the wikipedia page
url <- "https://en.wikipedia.org/w/index.php?title=2020_coronavirus_outbreak_in_the_United_States&oldid=944107102"
We will use a handy R package called rvest for the actual scraping. Let’s install and load that package.
# install the rvest package. This is a one-time operation.
# install.packages("rvest")
# load rvest package
library(rvest)
We next use the read_html function to read in the contents of the Wikipedia page into an R variable called page.
# read the page by calling the read_html function with the URL of the web page
page <- read_html(url)
The entire web page is now contained in the page variable.
A webpage can be a very complicated object and you can spend a lot of time learning about it. For our purposes, however, a simple framework is sufficient: just imagine the webpage as a box which contains many boxes, each of which may contain more boxes, and so on. These boxes are referred to as tags (or elements) in the technical literature.
Any text or numbers you see on a page is ‘inside’ one of these tags. In web scraping, the key challenge is to figure out which tag holds the data you are interested in. Once you identify the right tag, the rest is easy.
The easiest way to do this is to open the webpage in Chrome, right-click and choose ‘Inspect’, as shown below.

A new panel will open up:

Click anywhere in that panel and enter Ctrl-F on Windows (Command-F on the Mac). A little search box will open up.

Type in any text or data from the content you want to scrape. I typed ‘Jan 21’ in the search bar and it showed up in the search results:

The next step is the critical one.
Tables of data in web pages typically sit inside a ‘table’ tag so we scan up from “Jan 21” and stop at the first table tag we come across. It is easy to recognize tags, they have a ‘<’ before their name.

Next to the word ‘table’, we will usually see a ‘class’ or an ‘id’. Here, we see that our table is of class “wikitable”.

Armed with this, we next call the html_nodes function as shown below. This will grab all the tables on the page that are of class ‘wikitable’ since, in general, there may be many of them.
page %>%
# grab all the tables of class 'wikitable'.
html_nodes("table.wikitable")
{xml_nodeset (4)}
[1] <table class="wikitable">\n<caption>Cases in the United States per the CDC <sup id ...
[2] <table class="wikitable" style="text-align:right; font-size:85%;">\n<caption style ...
[3] <table class="wikitable sortable" style="float:left; text-align:center;"><tbody>\n ...
[4] <table class="wikitable sortable plainrowheaders" style="width: 100%;"><tbody>\n<t ...
Looks like there are 4 such tables on the page. Which one is ours?
Like before, we can go back to the web page on Chrome, right-click and choose ‘Inspect’, click anywhere on the pane that appears, enter Ctrl-F on Windows (Command-F on the Mac), and type ‘wikitable’ in the search box. The first search result is this table, which is not the one we want.

Press Enter once and it will take you to the second result (below), which is the one we want.

So we add a line to our code to select just the second table.
page %>%
# grab all the tables of class 'wikitable'.
html_nodes("table.wikitable") %>%
# select the 2nd table
.[[2]]
Now that we have the exact table we want, we convert it into a nice R dataframe using the html_table function and save it in a variable called covid_counts.
page %>%
# grab all the tables of class 'wikitable'.
html_nodes("table.wikitable") %>%
# select the 2nd table
.[[2]] %>%
# convert table into a dataframe and save in the variable 'covid_counts'.
html_table(fill = TRUE) -> covid_counts
Let’s take a look at the first few rows of covid_counts.
head(covid_counts, 3)
The column names are US geographic regions and the names of the states are in the first row of the dataframe. Let’s make the states as the names of the columns and remove that first row.
names(covid_counts) <- covid_counts[1,]
covid_counts <- covid_counts[-1,]
head(covid_counts, 3)
That looks better. Since there are 61 columns and horizontal scrolling is painful, let’s use names to get a quick sense for all the columns.
names(covid_counts)
[1] "Date" "AK" "AZ" "CA" "CO" "HI" "ID" "MT" "NM" "NV" "OR" "UT"
[13] "WA" "WY" "IA" "IL" "IN" "KS" "MI" "MN" "MO" "ND" "NE" "OH"
[25] "OK" "SD" "WI" "AL" "AR" "FL" "GA" "KY" "LA" "MS" "NC" "SC"
[37] "TN" "TX" "VA" "WV" "CT" "DC" "DE" "MA" "MD" "ME" "NH" "NJ"
[49] "NY" "PA" "RI" "VT" "GU" "PR" "VI" "New" "Cml" "New" "Cml" "New"
[61] "Cml"
Looks like the last 6 columns are summaries of the state-level data (you can confirm this by going to the Wikipedia page). We don’t need them so let’s remove them.
covid_counts <- covid_counts[,-(56:61)]
Next, let’s take a look at the last few rows to see if any summaries are lurking there. We can use the tail command. Alternatively, we can click on covid_counts in the Environment tab in RStudio.

A spreadsheet-like window will open up and display covid_counts. If you scroll to the bottom, you will notice 5 summary rows.

Let’s remove these 5 rows using the head function. head usually gets the first few rows but it can be used to get all but the last few, as shown below.
# by using the 'head' function with a negative number n
# you can get all but the last n
# this is handy since you don't need to calculate the # of rows
# and subtract n etc.
covid_counts <- head(covid_counts, -5)
Let’s take another look at covid_counts to see what else needs to be fixed.
head(covid_counts)
A few things that need fixing:
- The entries in the ‘Date’ column are represented as text but we want to convert them to R date objects so that R will treat them correctly (e.g., if we plot the data as a time series, R will know that Feb 1 is right after Jan 31).
- The numbers in the cells are represented as characters. We should convert them to numbers so we can add etc.
- There are lots of blanks. We should convert them to 0s.
The following code takes care of these fixes.
covid_counts %>%
# convert text to legit R date objects
mutate(Date = as.Date(Date, format = "%b%d")) %>%
# convert all columns except the 'Date' column to numeric
mutate_at(.vars = -1, as.numeric) %>%
# replace blanks with zeros
replace(., is.na(.), 0.0) -> covid_counts
head(covid_counts)
This looks much better and is in a form that’s ready for analysis.
This is just a matter of personal preference but I feel that it will be easier to make charts of counts by state etc if we can pivot this table so that it becomes long and narrow.
covid_counts %>%
pivot_longer(cols = -Date,
names_to = "State" ,
values_to = "Counts") %>%
arrange(State, Date)
This table has only 3 columns but many more rows. The ‘counts’ are daily new cases but it will probably be more informative to look at cumulative cases so let’s add that as a new column.
covid_counts %>%
pivot_longer(cols = -Date,
names_to = "State" ,
values_to = "Counts") %>%
arrange(State, Date) %>%
# group by State so that the cumultive
# calculation is for each state
group_by(State) %>%
# use the handy 'cumsum' function to
# "cumulatively sum" :-)
mutate(Cumulative = cumsum(Counts)) %>%
# undo the group-by and save it back
ungroup -> covid_counts
head(covid_counts)
Perfect!
In this “long and narrow” form, the dataset can be easily visualized in many ways. For example, here’s a chart of the growth in COVID-19 cases in the New England states from March 1st.
covid_counts %>%
# select only March data
filter(Date >= "2020-03-01") %>%
# remove the days before the first case was reported
filter(Cumulative > 0) %>%
# filter for New England states
filter(State %in% c('MA',
'CT',
'NH',
'RI',
'VT',
'ME')) %>%
ggplot(aes(x = Date, y = Cumulative, color = State)) +
geom_point() +
geom_line()

Since our objective is to show how to use R to scrape data rather than analyze COVID-19 trends, we will stop here.
One of the advantages of having all this code is that we can open RStudio and run the script daily with the push of a button and see how the COVID-19 numbers are changing in New England.
Scraping Prices from an Amazon Product Page
For our next example, I will show how to scrape a price from an Amazon product page.
Let’s say I want to extract the price from the Amazon page for an Wi-Fi extender I was looking at last week. One possible use for such a scraper is that I can run my R code every day to see how the price changes.

As before, let’s read in the contents of the page into an R variable.
url <- "https://www.amazon.com/gp/product/B010S6SG3S/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1"
# read in the URL into an R variable
page <- read_html(url)
Again, as before, we open up the page on Chrome, right-click, select ‘Inspect’, search for $56.96 on the pane that pops up, and note the tag that contains $56.96.

The name of the tag is ‘span’ and its id is “priceblock_ourprice”. We can use the html_nodes function as before but need to change it a bit.
In general, if the tag you are interested in has a class attribute, use html_nodes("name_of_tag.value_of_class"). If it has an id attribute, use html_nodes("name_of_tag#value_of_id"). Note the . vs the #.
In this case, we need to use html_nodes("span#priceblock_ourprice").
# grab any tags that contain the price
page %>%
html_nodes("span#priceblock_ourprice")
{xml_nodeset (1)}
[1] <span id="priceblock_ourprice" class="a-size-medium a-color-price priceBlockBuying ...
Looks like there’s just one tag that contains our price. Excellent. Now we can ‘reach inside’ the tag and extract its content with the html_text function.
page %>%
# grab any tags that contain the price
html_nodes("span#priceblock_ourprice") %>%
# extract the text content from within the tag
html_text
[1] "$56.96"
Done! We have scraped out the price.
LS0tCnRpdGxlOiAiQSBMaWdodG5pbmcgR3VpZGUgdG8gV2ViIFNjcmFwaW5nIERhdGEgV2l0aCBSIgpkYXRlOiAiTWFyY2ggMjEsIDIwMjAiCmF1dGhvcjogIlJhbWEgUmFtYWtyaXNobmFuIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyMgSW50cm9kdWN0aW9uCgpJbiB0aGUgY291cnNlIHNvIGZhciwgdGhlIGRhdGFzZXRzIHdlIGhhdmUgYW5hbHl6ZWQgaGF2ZSBiZWVuIHdlbGwtZm9ybWF0dGVkIENTViBmaWxlcy4gQXMgeW91IGtub3cgZnJvbSBleHBlcmllbmNlLCB0aGVzZSBhcmUgdmVyeSBlYXN5IHRvIGltcG9ydCBpbnRvIFIgdXNpbmcgdGhlIGByZWFkX2NzdmAgY29tbWFuZC4gCgpCdXQgcXVpdGUgb2Z0ZW4gaW4gcmVhbC13b3JsZCBwcm9qZWN0cywgdGhlIGRhdGEgd29uJ3QgYmUgYXZhaWxhYmxlIGFzIGVhc2lseS4gU29tZXRpbWVzIHRoZSBkYXRhIHlvdSBhcmUgbG9va2luZyBmb3IgbWF5IGJlIGVtYmVkZGVkIGluIG9uZSBvciBtb3JlIHdlYnBhZ2VzIGFuZCB5b3UgbWF5IG5lZWQgdG8gKipzY3JhcGUqKiB0aGVtIG91dC4gRm9yICpvbmUtb2ZmKiBzaXR1YXRpb25zLCBjdXR0aW5nIGFuZCBwYXN0aW5nIGEgdGFibGUgb2YgbnVtYmVycyBmcm9tIGEgd2VicGFnZSBpbnRvIEV4Y2VsIGNhbiB3b3JrIGp1c3QgZmluZS4gQnV0IGlmIHlvdSBuZWVkIHRvIGRvIHRoaXMgcmVwZWF0ZWRseSBvciBkbyBpdCBhY3Jvc3MgbWFueSBkaWZmZXJlbnQgc2l0ZXMsIGEgKnByb2dyYW1tYXRpYyogYXBwcm9hY2ggaXMgZWFzaWVyLiAKClRvIHRoYXQgZW5kLCB0aGlzIGJyaWVmIG5vdGUgZGVzY3JpYmVzICoqaG93IHRvIHVzZSBSIHRvIHNjcmFwZSBkYXRhIGZyb20gYSB3ZWIgcGFnZSoqLiAKCkkgd2lsbCBmaXJzdCBkZW1vbnN0cmF0ZSBob3cgdG8gc2NyYXBlIGRhdGEgZnJvbSBhIFdpa2lwZWRpYSBwYWdlIHRoYXQgaGFzIHVwLXRvLWRhdGUgQ09WSUQtMTkgY291bnRzIGZvciBhbGwgVVMgc3RhdGVzLiBJIGxlYXJuZWQgYWJvdXQgaXRzIGV4aXN0ZW5jZSBmcm9tIHRoaXMgW2Jsb2cgcG9zdF0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAyMC8wMy8wNS9jb3ZpZC0xOS1lcGlkZW1pb2xvZ3ktd2l0aC1yLykgKHdoaWNoIGFsc28gc2hvd3MsIHNvbWV3aGF0IHRlcnNlbHksIGhvdyB0byBzY3JhcGUgaXQpLiBOZXh0LCBJIHdpbGwgc2hvdyBhIHF1aWNrIGV4YW1wbGUgb2YgaG93IHRvIGV4dHJhY3QgcHJpY2VzIGZyb20gYW4gQW1hem9uIHByb2R1Y3QgcGFnZS4gCgpUaGUgYXBwcm9hY2ggaXMgZ2VuZXJhbCBhbmQgSSBob3BlIHlvdSBmaW5kIGl0IHVzZWZ1bCBmb3IgeW91ciBmdXR1cmUgcHJvamVjdHMsIHBlcmhhcHMgZXZlbiBmb3IgdGhlIGZpbmFsIHByb2plY3QgZm9yIHRoaXMgY291cnNlLiBJZiB5b3UgZG9uJ3QgaGF2ZSBwcm9ncmFtbWluZyBleHBlcmllbmNlLCB3ZWIgc2NyYXBpbmcgbWF5IGFwcGVhciBkaWZmaWN1bHQgYnV0IHJlc3QgYXNzdXJlZCwgaXQgaXMgbm90LiBCeSB0aGUgZW5kIG9mIHRoaXMgbm90ZSwgeW91IHNob3VsZCBiZSBhYmxlIHRvIHNjcmFwZSBwcm9kdWN0IHByaWNlcyBmcm9tIEFtYXpvbiBhbmQgc3dvb3AgaW4gdG8gYnV5IHdoZW4gdGhlIHByaWNlIGRyb3BzIDotKQoKTGV0J3MgZ2V0IHN0YXJ0ZWQuCgojIyMgU2NyYXBpbmcgYSBXaWtpcGVkaWEgUGFnZQoKRmlyc3QsIG9wZW4gW3RoaXMgV2lraXBlZGlhIHBhZ2VdKCJodHRwczovL2VuLndpa2lwZWRpYS5vcmcvdy9pbmRleC5waHA/dGl0bGU9MjAyMF9jb3JvbmF2aXJ1c19vdXRicmVha19pbl90aGVfVW5pdGVkX1N0YXRlcyZvbGRpZD05NDQxMDcxMDIiKSBpbiBDaHJvbWUgYW5kIHNjcm9sbCBkb3duIHRpbGwgeW91IGNvbWUgdG8gdGhpcyB0YWJsZToKCgoKIVtdKHdpa2lfcGFnZS5wbmcpCgpUaGlzIGlzIHRoZSBkYXRhIHdlIHdhbnQgdG8gZXh0cmFjdC4gRWFjaCByb3cgY29ycmVzcG9uZHMgdG8gYSBkYXRlLCBlYWNoIGNvbHVtbiBjb3JyZXNwb25kcyB0byBvbmUgb2YgdGhlIFVTIHN0YXRlcyBhbmQgZWFjaCBjZWxsIGhhcyB0aGUgbnVtYmVyIG9mICpuZXcqIENPVklELTE5IGNhc2VzIHJlcG9ydGVkIG9uIHRoYXQgZGF0ZSBpbiB0aGF0IHN0YXRlLiBXZSB3YW50IHRvIHNjcmFwZSB0aGlzIHRhYmxlIGZyb20gdGhlIHBhZ2UgYW5kIG1ha2UgaXQgaW50byBhIGRhdGFmcmFtZS4KClRvIHN0YXJ0LCBsZXQncyBsb2FkIGEgZmV3IGZhbWlsaWFyIHBhY2thZ2VzIGFuZCBhc3NpZ24gdGhlIFVSTCBvZiB0aGUgd2VicGFnZSB0byBhbiBSIHZhcmlhYmxlLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCiMgbG9hZCB0aGUgdXN1YWwgcGFja2FnZXMKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3Bsb3QyKQoKIyB0aGUgVVJMIG9mIHRoZSB3aWtpcGVkaWEgcGFnZSAKdXJsIDwtICJodHRwczovL2VuLndpa2lwZWRpYS5vcmcvdy9pbmRleC5waHA/dGl0bGU9MjAyMF9jb3JvbmF2aXJ1c19vdXRicmVha19pbl90aGVfVW5pdGVkX1N0YXRlcyZvbGRpZD05NDQxMDcxMDIiCmBgYAoKCgpXZSB3aWxsIHVzZSBhIGhhbmR5IFIgcGFja2FnZSBjYWxsZWQgYHJ2ZXN0YCBmb3IgdGhlIGFjdHVhbCBzY3JhcGluZy4gTGV0J3MgaW5zdGFsbCBhbmQgbG9hZCB0aGF0IHBhY2thZ2UuIAoKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQojIGluc3RhbGwgdGhlIHJ2ZXN0IHBhY2thZ2UuIFRoaXMgaXMgYSBvbmUtdGltZSBvcGVyYXRpb24uCiMgaW5zdGFsbC5wYWNrYWdlcygicnZlc3QiKQoKIyBsb2FkIHJ2ZXN0IHBhY2thZ2UKbGlicmFyeShydmVzdCkKYGBgCgoKV2UgbmV4dCB1c2UgdGhlIGByZWFkX2h0bWxgIGZ1bmN0aW9uIHRvIHJlYWQgaW4gdGhlIGNvbnRlbnRzIG9mIHRoZSBXaWtpcGVkaWEgcGFnZSBpbnRvIGFuIFIgdmFyaWFibGUgY2FsbGVkIGBwYWdlYC4gCgpgYGB7cn0KIyByZWFkIHRoZSBwYWdlIGJ5IGNhbGxpbmcgdGhlIHJlYWRfaHRtbCBmdW5jdGlvbiB3aXRoIHRoZSBVUkwgb2YgdGhlIHdlYiBwYWdlCnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKYGBgCgoKClRoZSBlbnRpcmUgd2ViIHBhZ2UgaXMgbm93IGNvbnRhaW5lZCBpbiB0aGUgYHBhZ2VgIHZhcmlhYmxlLiAKCkEgd2VicGFnZSBjYW4gYmUgYSB2ZXJ5IGNvbXBsaWNhdGVkIG9iamVjdCBhbmQgeW91IGNhbiBzcGVuZCBhIGxvdCBvZiB0aW1lIGxlYXJuaW5nIGFib3V0IGl0LiBGb3Igb3VyIHB1cnBvc2VzLCBob3dldmVyLCBhIHNpbXBsZSBmcmFtZXdvcmsgaXMgc3VmZmljaWVudDoganVzdCBpbWFnaW5lIHRoZSB3ZWJwYWdlIGFzIGEgYm94IHdoaWNoIGNvbnRhaW5zIG1hbnkgYm94ZXMsIGVhY2ggb2Ygd2hpY2ggbWF5IGNvbnRhaW4gbW9yZSBib3hlcywgYW5kIHNvIG9uLiBUaGVzZSBib3hlcyBhcmUgcmVmZXJyZWQgdG8gYXMgKip0YWdzKiogKG9yIGVsZW1lbnRzKSBpbiB0aGUgdGVjaG5pY2FsIGxpdGVyYXR1cmUuICAKCkFueSB0ZXh0IG9yIG51bWJlcnMgeW91IHNlZSBvbiBhIHBhZ2UgaXMgJ2luc2lkZScgb25lIG9mIHRoZXNlIHRhZ3MuICoqSW4gd2ViIHNjcmFwaW5nLCB0aGUga2V5IGNoYWxsZW5nZSBpcyB0byBmaWd1cmUgb3V0IHdoaWNoIHRhZyBob2xkcyB0aGUgZGF0YSB5b3UgYXJlIGludGVyZXN0ZWQgaW4qKi4gT25jZSB5b3UgaWRlbnRpZnkgdGhlIHJpZ2h0IHRhZywgdGhlIHJlc3QgaXMgZWFzeS4KClRoZSBlYXNpZXN0IHdheSB0byBkbyB0aGlzIGlzIHRvIG9wZW4gdGhlIHdlYnBhZ2UgaW4gQ2hyb21lLCByaWdodC1jbGljayBhbmQgY2hvb3NlICdJbnNwZWN0JywgYXMgc2hvd24gYmVsb3cuCgohW10od2lraTIucG5nKQoKCkEgbmV3IHBhbmVsIHdpbGwgb3BlbiB1cDoKCgohW10od2lraTMucG5nKQoKCkNsaWNrIGFueXdoZXJlIGluIHRoYXQgcGFuZWwgYW5kIGVudGVyIEN0cmwtRiBvbiBXaW5kb3dzIChDb21tYW5kLUYgb24gdGhlIE1hYykuICBBIGxpdHRsZSBzZWFyY2ggYm94IHdpbGwgb3BlbiB1cC4KCiFbXSh3aWtpNC5wbmcpCgoKVHlwZSBpbiBhbnkgdGV4dCBvciBkYXRhIGZyb20gdGhlIGNvbnRlbnQgeW91IHdhbnQgdG8gc2NyYXBlLiBJIHR5cGVkICdKYW4gMjEnIGluIHRoZSBzZWFyY2ggYmFyIGFuZCBpdCBzaG93ZWQgdXAgaW4gdGhlIHNlYXJjaCByZXN1bHRzOiAgICAgCgoKCiFbXSh3aWtpNS5wbmcpCgoKClRoZSBuZXh0IHN0ZXAgaXMgdGhlIGNyaXRpY2FsIG9uZS4KClRhYmxlcyBvZiBkYXRhIGluIHdlYiBwYWdlcyB0eXBpY2FsbHkgc2l0IGluc2lkZSBhICd0YWJsZScgdGFnIHNvIHdlIHNjYW4gKip1cCoqIGZyb20gIkphbiAyMSIgYW5kIHN0b3AgYXQgdGhlIGZpcnN0IHRhYmxlIHRhZyB3ZSBjb21lIGFjcm9zcy4gSXQgaXMgZWFzeSB0byByZWNvZ25pemUgdGFncywgdGhleSBoYXZlIGEgJzwnIGJlZm9yZSB0aGVpciBuYW1lLgoKIVtdKHdpa2k2LnBuZykKCgoKTmV4dCB0byB0aGUgd29yZCAndGFibGUnLCB3ZSB3aWxsIHVzdWFsbHkgc2VlIGEgJ2NsYXNzJyBvciBhbiAnaWQnLiBIZXJlLCB3ZSBzZWUgdGhhdCBvdXIgdGFibGUgaXMgb2YgY2xhc3MgIndpa2l0YWJsZSIuCgohW10od2lraTcucG5nKQoKCkFybWVkIHdpdGggdGhpcywgd2UgbmV4dCBjYWxsIHRoZSBgaHRtbF9ub2Rlc2AgZnVuY3Rpb24gYXMgc2hvd24gYmVsb3cuIFRoaXMgd2lsbCBncmFiICphbGwqIHRoZSB0YWJsZXMgb24gdGhlIHBhZ2UgdGhhdCBhcmUgb2YgY2xhc3MgJ3dpa2l0YWJsZScgc2luY2UsIGluIGdlbmVyYWwsIHRoZXJlIG1heSBiZSBtYW55IG9mIHRoZW0uIAoKYGBge3J9CnBhZ2UgJT4lIAogICMgZ3JhYiBhbGwgdGhlIHRhYmxlcyBvZiBjbGFzcyAnd2lraXRhYmxlJy4KICBodG1sX25vZGVzKCJ0YWJsZS53aWtpdGFibGUiKQpgYGAKCgpMb29rcyBsaWtlIHRoZXJlIGFyZSA0IHN1Y2ggdGFibGVzIG9uIHRoZSBwYWdlLiBXaGljaCBvbmUgaXMgb3Vycz8KCkxpa2UgYmVmb3JlLCB3ZSBjYW4gZ28gYmFjayB0byB0aGUgd2ViIHBhZ2Ugb24gQ2hyb21lLCByaWdodC1jbGljayBhbmQgY2hvb3NlICdJbnNwZWN0JywgY2xpY2sgYW55d2hlcmUgb24gdGhlIHBhbmUgdGhhdCBhcHBlYXJzLCBlbnRlciBDdHJsLUYgb24gV2luZG93cyAoQ29tbWFuZC1GIG9uIHRoZSBNYWMpLCBhbmQgdHlwZSAnd2lraXRhYmxlJyBpbiB0aGUgc2VhcmNoIGJveC4gVGhlIGZpcnN0IHNlYXJjaCByZXN1bHQgaXMgdGhpcyB0YWJsZSwgd2hpY2ggaXMgKipub3QqKiB0aGUgb25lIHdlIHdhbnQuCgohW10od2lraTgucG5nKQoKClByZXNzIEVudGVyIG9uY2UgYW5kIGl0IHdpbGwgdGFrZSB5b3UgdG8gdGhlIHNlY29uZCByZXN1bHQgKGJlbG93KSwgd2hpY2ggKmlzKiB0aGUgb25lIHdlIHdhbnQuCgohW10od2lraTkucG5nKQoKClNvIHdlIGFkZCBhIGxpbmUgdG8gb3VyIGNvZGUgdG8gc2VsZWN0IGp1c3QgdGhlICpzZWNvbmQqIHRhYmxlLgoKCgpgYGB7cn0KcGFnZSAlPiUgCiAgIyBncmFiIGFsbCB0aGUgdGFibGVzIG9mIGNsYXNzICd3aWtpdGFibGUnLgogIGh0bWxfbm9kZXMoInRhYmxlLndpa2l0YWJsZSIpICU+JSAKICAjIHNlbGVjdCB0aGUgMm5kIHRhYmxlCiAgLltbMl1dCmBgYAoKCk5vdyB0aGF0IHdlIGhhdmUgdGhlIGV4YWN0IHRhYmxlIHdlIHdhbnQsIHdlICBjb252ZXJ0IGl0IGludG8gYSBuaWNlIFIgZGF0YWZyYW1lIHVzaW5nIHRoZSBgaHRtbF90YWJsZWAgZnVuY3Rpb24gYW5kIHNhdmUgaXQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgYGNvdmlkX2NvdW50c2AuCgpgYGB7cn0KcGFnZSAlPiUgCiAgIyBncmFiIGFsbCB0aGUgdGFibGVzIG9mIGNsYXNzICd3aWtpdGFibGUnLgogIGh0bWxfbm9kZXMoInRhYmxlLndpa2l0YWJsZSIpICU+JSAKICAjIHNlbGVjdCB0aGUgMm5kIHRhYmxlCiAgLltbMl1dICU+JSAKICAjIGNvbnZlcnQgdGFibGUgaW50byBhIGRhdGFmcmFtZSBhbmQgc2F2ZSBpbiB0aGUgdmFyaWFibGUgJ2NvdmlkX2NvdW50cycuCiAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkgLT4gY292aWRfY291bnRzCmBgYAoKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgcm93cyBvZiBgY292aWRfY291bnRzYC4KCgpgYGB7cn0KaGVhZChjb3ZpZF9jb3VudHMsIDMpCmBgYAoKClRoZSBjb2x1bW4gbmFtZXMgYXJlIFVTIGdlb2dyYXBoaWMgcmVnaW9ucyBhbmQgdGhlIG5hbWVzIG9mIHRoZSBzdGF0ZXMgYXJlIGluIHRoZSBmaXJzdCByb3cgb2YgdGhlIGRhdGFmcmFtZS4gTGV0J3MgbWFrZSB0aGUgc3RhdGVzIGFzIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyBhbmQgcmVtb3ZlIHRoYXQgZmlyc3Qgcm93LgoKCmBgYHtyfQpuYW1lcyhjb3ZpZF9jb3VudHMpIDwtIGNvdmlkX2NvdW50c1sxLF0KY292aWRfY291bnRzIDwtIGNvdmlkX2NvdW50c1stMSxdCmhlYWQoY292aWRfY291bnRzLCAzKQpgYGAKCgpUaGF0IGxvb2tzIGJldHRlci4gU2luY2UgdGhlcmUgYXJlIDYxIGNvbHVtbnMgYW5kIGhvcml6b250YWwgc2Nyb2xsaW5nIGlzIHBhaW5mdWwsIGxldCdzIHVzZSBgbmFtZXNgIHRvIGdldCBhIHF1aWNrIHNlbnNlIGZvciBhbGwgdGhlIGNvbHVtbnMuCgpgYGB7cn0KbmFtZXMoY292aWRfY291bnRzKQpgYGAKCgpMb29rcyBsaWtlIHRoZSBsYXN0IDYgY29sdW1ucyBhcmUgc3VtbWFyaWVzIG9mIHRoZSBzdGF0ZS1sZXZlbCBkYXRhICh5b3UgY2FuIGNvbmZpcm0gdGhpcyBieSBnb2luZyB0byB0aGUgV2lraXBlZGlhIHBhZ2UpLiBXZSBkb24ndCBuZWVkIHRoZW0gc28gbGV0J3MgcmVtb3ZlIHRoZW0uCgpgYGB7cn0KY292aWRfY291bnRzIDwtIGNvdmlkX2NvdW50c1ssLSg1Njo2MSldCmBgYAoKCk5leHQsIGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBsYXN0IGZldyByb3dzIHRvIHNlZSBpZiBhbnkgc3VtbWFyaWVzIGFyZSBsdXJraW5nIHRoZXJlLiBXZSBjYW4gdXNlIHRoZSBgdGFpbGAgY29tbWFuZC4gQWx0ZXJuYXRpdmVseSwgd2UgY2FuIGNsaWNrIG9uIGBjb3ZpZF9jb3VudHNgIGluIHRoZSBFbnZpcm9ubWVudCB0YWIgaW4gUlN0dWRpby4KCiFbXSh3aWtpMTIucG5nKQoKCkEgc3ByZWFkc2hlZXQtbGlrZSB3aW5kb3cgd2lsbCBvcGVuIHVwIGFuZCBkaXNwbGF5IGBjb3ZpZF9jb3VudHNgLiBJZiB5b3Ugc2Nyb2xsIHRvIHRoZSBib3R0b20sIHlvdSB3aWxsIG5vdGljZSA1IHN1bW1hcnkgcm93cy4KCiFbXSh3aWtpMTMucG5nKQoKCkxldCdzIHJlbW92ZSB0aGVzZSA1IHJvd3MgdXNpbmcgdGhlIGBoZWFkYCBmdW5jdGlvbi4gYGhlYWRgIHVzdWFsbHkgZ2V0cyB0aGUgZmlyc3QgZmV3IHJvd3MgYnV0IGl0IGNhbiBiZSB1c2VkIHRvIGdldCBhbGwgYnV0IHRoZSBsYXN0IGZldywgYXMgc2hvd24gYmVsb3cuCgoKYGBge3J9CiMgYnkgdXNpbmcgdGhlICdoZWFkJyBmdW5jdGlvbiB3aXRoIGEgbmVnYXRpdmUgbnVtYmVyIG4KIyB5b3UgY2FuIGdldCBhbGwgYnV0IHRoZSBsYXN0IG4KIyB0aGlzIGlzIGhhbmR5IHNpbmNlIHlvdSBkb24ndCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgIyBvZiByb3dzCiMgYW5kIHN1YnRyYWN0IG4gZXRjLgoKY292aWRfY291bnRzIDwtIGhlYWQoY292aWRfY291bnRzLCAtNSkKCmBgYAoKCkxldCdzIHRha2UgYW5vdGhlciBsb29rIGF0IGBjb3ZpZF9jb3VudHNgIHRvIHNlZSB3aGF0IGVsc2UgbmVlZHMgdG8gYmUgZml4ZWQuCgoKYGBge3J9CmhlYWQoY292aWRfY291bnRzKQpgYGAKCgpBIGZldyB0aGluZ3MgdGhhdCBuZWVkIGZpeGluZzoKCiogVGhlIGVudHJpZXMgaW4gdGhlICdEYXRlJyBjb2x1bW4gYXJlIHJlcHJlc2VudGVkIGFzIHRleHQgYnV0IHdlIHdhbnQgdG8gY29udmVydCB0aGVtIHRvIFIgZGF0ZSBvYmplY3RzIHNvIHRoYXQgUiB3aWxsIHRyZWF0IHRoZW0gY29ycmVjdGx5IChlLmcuLCBpZiB3ZSBwbG90IHRoZSBkYXRhIGFzIGEgdGltZSBzZXJpZXMsIFIgd2lsbCBrbm93IHRoYXQgRmViIDEgaXMgcmlnaHQgYWZ0ZXIgSmFuIDMxKS4KKiBUaGUgbnVtYmVycyBpbiB0aGUgY2VsbHMgYXJlIHJlcHJlc2VudGVkIGFzIGNoYXJhY3RlcnMuIFdlIHNob3VsZCBjb252ZXJ0IHRoZW0gdG8gbnVtYmVycyBzbyB3ZSBjYW4gYWRkIGV0Yy4KKiBUaGVyZSBhcmUgbG90cyBvZiBibGFua3MuIFdlIHNob3VsZCBjb252ZXJ0IHRoZW0gdG8gMHMuCgpUaGUgZm9sbG93aW5nIGNvZGUgdGFrZXMgY2FyZSBvZiB0aGVzZSBmaXhlcy4KCmBgYHtyfQpjb3ZpZF9jb3VudHMgJT4lCiAgIyBjb252ZXJ0IHRleHQgdG8gbGVnaXQgUiBkYXRlIG9iamVjdHMKICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgZm9ybWF0ID0gIiViJWQiKSkgJT4lCiAgIyBjb252ZXJ0IGFsbCBjb2x1bW5zIGV4Y2VwdCB0aGUgJ0RhdGUnIGNvbHVtbiB0byBudW1lcmljCiAgbXV0YXRlX2F0KC52YXJzID0gLTEsIGFzLm51bWVyaWMpICU+JQogICMgcmVwbGFjZSBibGFua3Mgd2l0aCB6ZXJvcwogIHJlcGxhY2UoLiwgaXMubmEoLiksIDAuMCkgLT4gY292aWRfY291bnRzCgpoZWFkKGNvdmlkX2NvdW50cykKYGBgCgoKVGhpcyBsb29rcyBtdWNoIGJldHRlciBhbmQgaXMgaW4gYSBmb3JtIHRoYXQncyByZWFkeSBmb3IgYW5hbHlzaXMuIAoKVGhpcyBpcyBqdXN0IGEgbWF0dGVyIG9mIHBlcnNvbmFsIHByZWZlcmVuY2UgYnV0IEkgZmVlbCB0aGF0IGl0IHdpbGwgYmUgIGVhc2llciB0byBtYWtlIGNoYXJ0cyBvZiBjb3VudHMgYnkgc3RhdGUgZXRjIGlmIHdlIGNhbiAqcGl2b3QqIHRoaXMgdGFibGUgc28gdGhhdCBpdCBiZWNvbWVzIGxvbmcgYW5kIG5hcnJvdy4KCmBgYHtyfQpjb3ZpZF9jb3VudHMgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtRGF0ZSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiU3RhdGUiICwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIkNvdW50cyIpICU+JQogIGFycmFuZ2UoU3RhdGUsIERhdGUpCmBgYAoKVGhpcyB0YWJsZSBoYXMgb25seSAzIGNvbHVtbnMgYnV0IG1hbnkgbW9yZSByb3dzLiBUaGUgJ2NvdW50cycgYXJlIGRhaWx5ICpuZXcqIGNhc2VzIGJ1dCBpdCB3aWxsIHByb2JhYmx5IGJlIG1vcmUgaW5mb3JtYXRpdmUgdG8gbG9vayBhdCAqKmN1bXVsYXRpdmUqKiBjYXNlcyBzbyBsZXQncyBhZGQgdGhhdCBhcyBhIG5ldyBjb2x1bW4uCgpgYGB7cn0KY292aWRfY291bnRzICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLURhdGUsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlN0YXRlIiAsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJDb3VudHMiKSAlPiUKICBhcnJhbmdlKFN0YXRlLCBEYXRlKSAlPiUgCiAgIyBncm91cCBieSBTdGF0ZSBzbyB0aGF0IHRoZSBjdW11bHRpdmUgCiAgIyBjYWxjdWxhdGlvbiBpcyBmb3IgZWFjaCBzdGF0ZQogIGdyb3VwX2J5KFN0YXRlKSAlPiUKICAjIHVzZSB0aGUgaGFuZHkgJ2N1bXN1bScgZnVuY3Rpb24gdG8KICAjICJjdW11bGF0aXZlbHkgc3VtIiA6LSkKICBtdXRhdGUoQ3VtdWxhdGl2ZSA9IGN1bXN1bShDb3VudHMpKSAlPiUKICAjIHVuZG8gdGhlIGdyb3VwLWJ5IGFuZCBzYXZlIGl0IGJhY2sKICB1bmdyb3VwIC0+IGNvdmlkX2NvdW50cwoKaGVhZChjb3ZpZF9jb3VudHMpCmBgYAoKClBlcmZlY3QhCgpJbiB0aGlzICJsb25nIGFuZCBuYXJyb3ciIGZvcm0sIHRoZSBkYXRhc2V0IGNhbiBiZSBlYXNpbHkgdmlzdWFsaXplZCBpbiBtYW55IHdheXMuIEZvciBleGFtcGxlLCBoZXJlJ3MgYSBjaGFydCBvZiB0aGUgZ3Jvd3RoIGluIENPVklELTE5IGNhc2VzIGluIHRoZSBOZXcgRW5nbGFuZCBzdGF0ZXMgZnJvbSBNYXJjaCAxc3QuIAoKYGBge3J9CmNvdmlkX2NvdW50cyAlPiUKICAjIHNlbGVjdCBvbmx5IE1hcmNoIGRhdGEKICBmaWx0ZXIoRGF0ZSA+PSAiMjAyMC0wMy0wMSIpICU+JQogICMgcmVtb3ZlIHRoZSBkYXlzIGJlZm9yZSB0aGUgZmlyc3QgY2FzZSB3YXMgcmVwb3J0ZWQKICBmaWx0ZXIoQ3VtdWxhdGl2ZSA+IDApICU+JQogICMgZmlsdGVyIGZvciBOZXcgRW5nbGFuZCBzdGF0ZXMKICBmaWx0ZXIoU3RhdGUgJWluJSBjKCdNQScsCiAgICAgICAgICAgICAgICAgICAgICAnQ1QnLAogICAgICAgICAgICAgICAgICAgICAgJ05IJywKICAgICAgICAgICAgICAgICAgICAgICdSSScsCiAgICAgICAgICAgICAgICAgICAgICAnVlQnLAogICAgICAgICAgICAgICAgICAgICAgJ01FJykpICU+JQogIGdncGxvdChhZXMoeCA9IERhdGUsIHkgPSBDdW11bGF0aXZlLCBjb2xvciA9IFN0YXRlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgCmBgYAoKU2luY2Ugb3VyIG9iamVjdGl2ZSBpcyB0byBzaG93IGhvdyB0byB1c2UgUiB0byBzY3JhcGUgZGF0YSByYXRoZXIgdGhhbiBhbmFseXplIENPVklELTE5IHRyZW5kcywgd2Ugd2lsbCBzdG9wIGhlcmUuIAoKT25lIG9mIHRoZSBhZHZhbnRhZ2VzIG9mIGhhdmluZyBhbGwgdGhpcyBjb2RlIGlzIHRoYXQgd2UgY2FuIG9wZW4gUlN0dWRpbyBhbmQgcnVuIHRoZSBzY3JpcHQgZGFpbHkgd2l0aCB0aGUgcHVzaCBvZiBhIGJ1dHRvbiBhbmQgc2VlIGhvdyB0aGUgQ09WSUQtMTkgbnVtYmVycyBhcmUgY2hhbmdpbmcgaW4gTmV3IEVuZ2xhbmQuCgojIyMgU2NyYXBpbmcgUHJpY2VzIGZyb20gYW4gQW1hem9uIFByb2R1Y3QgUGFnZQoKRm9yIG91ciBuZXh0IGV4YW1wbGUsIEkgd2lsbCBzaG93IGhvdyB0byBzY3JhcGUgYSBwcmljZSBmcm9tIGFuIEFtYXpvbiBwcm9kdWN0IHBhZ2UuIAoKTGV0J3Mgc2F5IEkgd2FudCB0byBleHRyYWN0IHRoZSBwcmljZSBmcm9tIFt0aGUgQW1hem9uIHBhZ2UgZm9yIGFuIFdpLUZpIGV4dGVuZGVyIEkgd2FzIGxvb2tpbmcgYXQgbGFzdCB3ZWVrXShodHRwczovL3d3dy5hbWF6b24uY29tL2dwL3Byb2R1Y3QvQjAxMFM2U0czUy9yZWY9cHB4X3lvX2R0X2JfYXNpbl90aXRsZV9vMDBfczAwP2llPVVURjgmcHNjPTEpLiBPbmUgcG9zc2libGUgdXNlIGZvciBzdWNoIGEgc2NyYXBlciBpcyB0aGF0IEkgY2FuIHJ1biBteSBSIGNvZGUgZXZlcnkgZGF5IHRvIHNlZSBob3cgdGhlIHByaWNlIGNoYW5nZXMuCgohW10od2lraTEwLnBuZykKCkFzIGJlZm9yZSwgbGV0J3MgcmVhZCBpbiB0aGUgY29udGVudHMgb2YgdGhlIHBhZ2UgaW50byBhbiBSIHZhcmlhYmxlLgoKYGBge3J9CnVybCA8LSAiaHR0cHM6Ly93d3cuYW1hem9uLmNvbS9ncC9wcm9kdWN0L0IwMTBTNlNHM1MvcmVmPXBweF95b19kdF9iX2FzaW5fdGl0bGVfbzAwX3MwMD9pZT1VVEY4JnBzYz0xIgogIAojIHJlYWQgaW4gdGhlIFVSTCBpbnRvIGFuIFIgdmFyaWFibGUKcGFnZSA8LSByZWFkX2h0bWwodXJsKQpgYGAKCkFnYWluLCBhcyBiZWZvcmUsIHdlIG9wZW4gdXAgdGhlIHBhZ2Ugb24gQ2hyb21lLCByaWdodC1jbGljaywgc2VsZWN0ICdJbnNwZWN0Jywgc2VhcmNoIGZvciAkNTYuOTYgb24gdGhlIHBhbmUgdGhhdCBwb3BzIHVwLCBhbmQgbm90ZSB0aGUgdGFnIHRoYXQgY29udGFpbnMgJDU2Ljk2LgoKIVtdKHdpa2kxMS5wbmcpCgpUaGUgbmFtZSBvZiB0aGUgdGFnIGlzICdzcGFuJyBhbmQgaXRzIGlkIGlzICJwcmljZWJsb2NrX291cnByaWNlIi4gV2UgY2FuIHVzZSB0aGUgYGh0bWxfbm9kZXNgIGZ1bmN0aW9uIGFzIGJlZm9yZSBidXQgbmVlZCB0byBjaGFuZ2UgaXQgYSBiaXQuCgpJbiBnZW5lcmFsLCBpZiB0aGUgdGFnIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBoYXMgYSBgY2xhc3NgIGF0dHJpYnV0ZSwgdXNlIGBodG1sX25vZGVzKCJuYW1lX29mX3RhZy52YWx1ZV9vZl9jbGFzcyIpYC4gSWYgaXQgaGFzIGFuIGBpZGAgYXR0cmlidXRlLCB1c2UgYGh0bWxfbm9kZXMoIm5hbWVfb2ZfdGFnI3ZhbHVlX29mX2lkIilgLiBOb3RlIHRoZSBgLmAgdnMgdGhlIGAjYC4KCkluIHRoaXMgY2FzZSwgd2UgbmVlZCB0byB1c2UgIGBodG1sX25vZGVzKCJzcGFuI3ByaWNlYmxvY2tfb3VycHJpY2UiKWAuCgpgYGB7cn0KIyBncmFiIGFueSB0YWdzIHRoYXQgY29udGFpbiB0aGUgcHJpY2UKcGFnZSAlPiUgCiAgaHRtbF9ub2Rlcygic3BhbiNwcmljZWJsb2NrX291cnByaWNlIikKYGBgCgpMb29rcyBsaWtlIHRoZXJlJ3MganVzdCBvbmUgdGFnIHRoYXQgY29udGFpbnMgb3VyIHByaWNlLiBFeGNlbGxlbnQuIE5vdyB3ZSBjYW4gJ3JlYWNoIGluc2lkZScgdGhlIHRhZyBhbmQgZXh0cmFjdCBpdHMgY29udGVudCB3aXRoIHRoZSBgaHRtbF90ZXh0YCBmdW5jdGlvbi4KCmBgYHtyfQpwYWdlICU+JSAKICAjIGdyYWIgYW55IHRhZ3MgdGhhdCBjb250YWluIHRoZSBwcmljZQogIGh0bWxfbm9kZXMoInNwYW4jcHJpY2VibG9ja19vdXJwcmljZSIpICU+JSAKICAjIGV4dHJhY3QgdGhlIHRleHQgY29udGVudCBmcm9tIHdpdGhpbiB0aGUgdGFnCiAgaHRtbF90ZXh0CmBgYAoKRG9uZSEgV2UgaGF2ZSBzY3JhcGVkIG91dCB0aGUgcHJpY2UuCgojIyMgQ29uY2x1c2lvbgoKVGhlcmUgYXJlIGxvdHMgb2YgbnVhbmNlcyB0byB3ZWIgc2NyYXBpbmcgYW5kIHdlIGhhdmUgYmFyZWx5IHNjcmF0Y2hlZCB0aGUgc3VyZmFjZSBidXQgdGhpcyBub3RlIHNob3VsZCBiZSBlbm91Z2ggZm9yIHlvdSB0byBnZXQgc3RhcnRlZCBpZiB5b3UgaGF2ZW4ndCBkb25lIGl0IGJlZm9yZS4gTm90ZSwgaG93ZXZlciwgdGhhdCB3ZWJwYWdlcyBrZWVwIGNoYW5naW5nIGFuZCBpZiB5b3UgbmVlZCB0byBzY3JhcGUgZGF0YSBvdmVyIGEgbG9uZyB0aW1lLXBlcmlvZCwgdGhlc2UgY2hhbmdlcyBtYXkgJ2JyZWFrJyB5b3VyIGNvZGUgYW5kIHlvdSB3aWxsIG5lZWQgdG8gcmVwZWF0IHRoZSBwcm9jZXNzIHdlIHdlbnQgdGhyb3VnaCB0byB1cGRhdGUgdGhlIGNvZGUuCgpGb3IgY29udmVuaWVuY2UsIEkgaGF2ZSBjb2xsZWN0ZWQgYWxsIHRoZSBjb2RlIGluIHRoaXMgbm90ZSBhcyBhbiBSIHNjcmlwdCBbaGVyZV0oc2ltcGxlX3dlYl9zY3JhcGluZy5SKS4gSGFwcHkgc2NyYXBpbmchCgo=